﻿#include "command_impl.hh"
#include "../box/service_box.hh"
#include "../detail/lang_impl.hh"
#include "../repo/src/include/root/coroutine/coroutine.h"
#include "../util/time_util.hh"
#include "command_manager.hh"

kratos::service::CommandImpl::CommandImpl(CommandManager *manager) {
  manager_ = manager;
}

kratos::service::CommandImpl::~CommandImpl() {
  // 将自己从容器中删除
  for (auto it : proc_map_) {
    if (manager_) {
      manager_->del_command(it.first);
    }
  }
  proc_map_.clear();
  // 取消所有协程
  auto temp = process_map_;
  for (auto it : temp) {
    if (it.second.coid) {
      coro_cancel(it.second.coid);
      coro_wakeup(it.second.coid);
    }
  }
  process_map_.clear();
}

auto kratos::service::CommandImpl::wait_for(const std::string &name,
                                            std::time_t max_exec_time,
                                            CommandProc proc) -> bool {
  if (name.empty() || !proc) {
    return false;
  }
  proc_map_[name] = {proc, 0, max_exec_time};
  // 将自己注册到容器中
  if (manager_) {
    manager_->add_command(name, this);
  }
  return true;
}

auto kratos::service::CommandImpl::wait_path(const std::string& path,
  std::time_t max_exec_time, CommandProc proc) -> bool {
  if (path.empty() || !proc) {
      return false;
  }
  proc_map_[path] = { proc, 0, max_exec_time };
  // 将自己注册到容器中
  if (manager_) {
      manager_->add_path(path, this);
  }
  return true;
}

auto kratos::service::CommandImpl::update(std::time_t ms) -> void {
  if (process_map_.empty()) {
    return;
  }
  auto it = process_map_.begin();
  if (it->second.deadline > ms) {
    return;
  }
  auto id = it->first;
  auto coid = it->second.coid;
  coro_cancel(coid);
  coro_wakeup(coid);
  // 每帧销毁一个，防止迭代器失效
  auto double_check_it = process_map_.find(id);
  if (double_check_it != process_map_.end()) {
    process_map_.erase(double_check_it);
  }
}

auto kratos::service::CommandImpl::on_command(http::HttpCallPtr callPtr,
                                              const std::string &name,
                                              const std::string &content)
    -> bool {
  auto it = proc_map_.find(name);
  if (it == proc_map_.end()) {
    return false;
  } else {
    if (it->second.count > 0) {
      return false;
    }
  }
  coro_start([&](void *) {
    auto proc = it->second.proc;
    auto request = content;
    auto cmd_name = name;
    auto call = callPtr;
    auto proc_id = proc_id_++;
    process_map_[proc_id] = {
        cmd_name, util::get_os_time_millionsecond() + it->second.max_exec_time,
        call, coro_id()};
    proc_map_[cmd_name].count += 1;
    try {
      auto return_str = proc(request);
      if (!call.expired()) {
        auto shared_call = call.lock();
        auto response = shared_call->get_response().lock();
        response->set_content(return_str);
        response->set_status_code(200);
        shared_call->finish();
      }
    } catch (CoroCancelException &) {
    } catch (std::exception &ex) {
      if (manager_ && manager_->get_box()) {
        manager_->get_box()->write_log(lang::LangID::LANG_COMMAND_EXCEPTION,
                                       klogger::Logger::WARNING,
                                       cmd_name.c_str(), ex.what());
      }
      if (!call.expired()) {
        auto response = call.lock()->get_response().lock();
        response->set_content("{\"error\" : \"Internal error\"}");
        response->set_status_code(500);
        call.lock()->finish();
      }
    }
    proc_map_[cmd_name].count -= 1;
    process_map_.erase(proc_id);
  });
  return true;
}

auto kratos::service::CommandImpl::detach() -> void { manager_ = nullptr; }
